package com.jumi.microservice.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jumi.microservice.common.base.dto.ResponseResult;
import com.jumi.microservice.common.constant.InventoryRedisKey;
import com.jumi.microservice.constant.*;
import com.jumi.microservice.domain.*;
import com.jumi.microservice.mapper.GoodsStockLogMapper;
import com.jumi.microservice.mapper.GoodsStockMapper;
import com.jumi.microservice.mapper.GoodsWarehouseStockMapper;
import com.jumi.microservice.material.service.IJmWarehouseSeqService;
import com.jumi.microservice.stock.CancelOrderUpdaterFactory;
import com.jumi.microservice.stock.StockUpdater;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 库存中心对订单中心提供的接口实现类
 *
 * @author Administrator
 */
@DubboService
@Transactional(rollbackFor = Exception.class)
public class OrderInventoryServiceImpl implements IOrderInventoryService {

    private static final Logger logger = LoggerFactory.getLogger(OrderInventoryServiceImpl.class);

    @Autowired
    private GoodsStockMapper goodsStockMapper;

    @Autowired
    private GoodsStockLogMapper goodsStockLogMapper;

    @Autowired
    private GoodsWarehouseStockMapper goodsWarehouseStockMapper;

    @DubboReference
    private BasicDataService basicDataService;

    @DubboReference
    private IJmWarehouseSeqService jmWarehouseSeqService;

    @Autowired
    CancelOrderUpdaterFactory cancelOrderUpdaterFactory;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DefaultRedisScript<List> redisScript;

    /**
     * 通知库存中心，“提交订单”事件
     *
     * @param orderItems 订单DTO集合
     * @return List<OrderInventoryDTO> 处理结果
     */
    @Override
    public ResponseResult<OrderInventoryDTO> informSubmitOrderEvent(List<OrderItemDTO> orderItems) throws Exception {
        logger.info("提交订单获取到的原参数信息:{}", JSONObject.toJSONString(orderItems));
        //封装给订单返回的数据实体
        OrderInventoryDTO orderInventory = new OrderInventoryDTO();
        List<OrderInventoryItemDTO> orderInventories = new ArrayList<>();
        orderInventory.setOrderInventoryItems(orderInventories);

        //封装操作数据库的实体集合
        List<GoodsStockDTO> goodsStocks = new ArrayList<>();

        //获取订单中的商城skuid集合,查询商城服务获取商城sku的基本信息
        //Map<Long, SkuDTO> skuMap = basicDataService.getSkuInfoByIds(getGoodsSkuIds(orderItems));

        List<Long> goodsSkuIds = getGoodsSkuIds(orderItems);
        List<String> skuIdsKey = goodsSkuIds.stream().map(skuId -> "JUMI:SKU:".concat(skuId.toString())).collect(Collectors.toList());
        List<Object> skuList = redisTemplate.opsForValue().multiGet(skuIdsKey);

        Map<Long, SkuDTO> skuMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(skuList)) {
            skuList.stream().filter(Objects::nonNull).forEach(sku -> {
                SkuDTO skuDTO = (SkuDTO) sku;
                if (!skuMap.containsKey(skuDTO.getId())) {
                    skuMap.put(skuDTO.getId(), skuDTO);
                }
            });
        }

        List<Long> subSkuIds = new ArrayList<>(skuMap.keySet());
        if (goodsSkuIds.size() > subSkuIds.size()) {
            // 将redis没有的sku通过服务调用获取
            if (subSkuIds.size() > NumberConstant.NUMBER_ZERO.intValue()) {
                goodsSkuIds.removeAll(subSkuIds);
            }
            Map<Long, SkuDTO> skuMapDb = basicDataService.getSkuInfoByIds(goodsSkuIds);
            skuMap.putAll(skuMapDb);
        }
        logger.info("提交订单获取到的商品的信息:{}", JSONObject.toJSONString(skuMap));

        LambdaQueryWrapper<GoodsStockDO> goodsStockWrapper = Wrappers.lambdaQuery();
        goodsStockWrapper.in(GoodsStockDO::getGoodsSkuId, goodsSkuIds);
        List<GoodsStockDO> goodsStockDoList = goodsStockMapper.selectList(goodsStockWrapper);

        Map<Long, GoodsStockDO> goodsStockDOMap = new HashMap<>();
        if (goodsStockDoList.size() > NumberConstant.NUMBER_ZERO.intValue()) {
            goodsStockDoList.stream().forEach(goodsStockDO -> goodsStockDOMap.put(goodsStockDO.getGoodsSkuId(), goodsStockDO));
        }

        //根据地区ID查询物料服务获取仓库ID和顺序(优化点-可以设计从redis缓存中查询)
        ResponseResult<List<Map<String, Object>>> responseResult = jmWarehouseSeqService.findByAreaId(orderItems.get(0).getAreaId());
        List<Map<String, Object>> warehouseList = responseResult.getData();

        //遍历订单明细信息
        for (OrderItemDTO orderItem : orderItems) {
            SkuDTO skuDto = skuMap.get(orderItem.getGoodsSkuId());
            OrderInventoryItemDTO orderInventoryItem = buildOrderInventoryItem(skuDto);

            //根据sku ID查询库存表信息
            GoodsStockDO goodsStock = goodsStockDOMap.get(orderItem.getGoodsSkuId());
            //如果根据sku id查询不到记录,说明商品上架但是还未添加库存信息,此时需要给订单返回库存不足的提示信息
            if (goodsStock == null) {
                orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                orderInventoryItem.setIsPre(StatusEnum.STATUS_NO.getCode());
                orderInventories.add(orderInventoryItem);

                return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                        OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
            }

            GoodsStockDTO goodsStockDto = new GoodsStockDTO();
            goodsStockDto.setId(goodsStockDto.getId());
            goodsStockDto.setGoodsId(goodsStock.getGoodsId());
            goodsStockDto.setGoodsSkuId(goodsStock.getGoodsSkuId());
            goodsStockDto.setGoodsSkuCode(goodsStock.getGoodsSkuCode());
            goodsStockDto.setVersion(goodsStock.getVersion());

            //purchaseQuantity为订单中购买的商品数量
            Integer purchaseQuantity = orderItem.getPurchaseQuantity();
            //应发仓库ID
            Long shouldWarehouseId = null;
            //实发仓库ID
            Long actualWarehouseId = null;

            //首先判断总库存是否充足,如果总库存充足则需要查看每个仓库的库存是否充足
            if (purchaseQuantity <= goodsStock.getGoodsSkuStorage()) {
                //销售库存减去购买数量
                goodsStockDto.setGoodsSkuStorage(goodsStock.getGoodsSkuStorage() - purchaseQuantity);
                //锁定库存加上购买数量
                goodsStockDto.setLockedStorage(goodsStock.getLockedStorage() + purchaseQuantity);
                goodsStockDto.setPurchaseQuantity(purchaseQuantity);
                goodsStockDto.setIsPre(StatusEnum.STATUS_NO.getCode());

                //封装订单流水表实体
                GoodsStockLogDTO goodsStockLogDto = new GoodsStockLogDTO();
                goodsStockLogDto.setGoodsId(goodsStock.getGoodsId());
                goodsStockLogDto.setGoodsSkuId(goodsStock.getGoodsSkuId());
                goodsStockLogDto.setLockedStorage(purchaseQuantity);
                goodsStockLogDto.setInventoryType(InventoryType.REAL_INVENTORY.getCode());
                goodsStockLogDto.setUserAccountId(orderItem.getUserAccountId());
                goodsStockLogDto.setRequestTime(orderItem.getRequestTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());

                if (!skuDto.getGoodsType().equals(GoodsTypeEnum.GOODS_TYPE_THREE.getCode())) {
                    //自营商品
                    goodsStockDto.setIsShelf(StatusEnum.STATUS_YES.getCode());

                    //根据地区ID查询物料服务获取仓库ID和顺序(优化点-可以设计从redis缓存中查询)
           /*         ResponseResult<List<Map<String, Object>>> responseResult = jmWarehouseSeqService.findByAreaId(orderItem.getAreaId());
                    List<Map<String, Object>> warehouseList = responseResult.getData();*/
                    /*if (warehouseList != null && warehouseList.size() > NumberConstant.NUMBER_ZERO.intValue()) {
                        shouldWarehouseId = Long.valueOf(String.valueOf(warehouseList.get(NumberConstant.NUMBER_ZERO.intValue()).get("warehouseId")));
                    }*/

                    //循环依次判断哪个仓库的库存充足,怎么作为实际库存发货仓库
                    GoodsWarehouseStockDO goodsWarehouseStock = null;
                    if (warehouseList != null) {
                        for (Map<String, Object> warehouse : warehouseList) {
                            Long warehouseId = Long.valueOf(String.valueOf(warehouse.get("warehouseId")));
                            LambdaQueryWrapper<GoodsWarehouseStockDO> warehouseStockWrapper = Wrappers.lambdaQuery();
                            warehouseStockWrapper.eq(orderItem.getGoodsSkuId() != null, GoodsWarehouseStockDO::getGoodsSkuId, orderItem.getGoodsSkuId());
                            warehouseStockWrapper.eq(warehouseId != null, GoodsWarehouseStockDO::getWarehouseId, warehouseId);

                            goodsWarehouseStock = goodsWarehouseStockMapper.selectOne(warehouseStockWrapper);
                            if (goodsWarehouseStock == null) {
                                continue;
                            }

                            if (shouldWarehouseId == null) {
                                shouldWarehouseId = warehouseId;
                            }

                            if (purchaseQuantity <= goodsWarehouseStock.getSaleStockQuantity()) {
                                actualWarehouseId = warehouseId;
                                break;
                            }
                        }
                    }
                    orderInventoryItem.setShouldWarehouseId(shouldWarehouseId);
                    orderInventoryItem.setActualWarehouseId(actualWarehouseId);
                    orderInventoryItem.setIsPre(StatusEnum.STATUS_NO.getCode());

                    //提示该地区没库存
                    if (Objects.isNull(shouldWarehouseId) && Objects.isNull(actualWarehouseId)) {
                        orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventories.add(orderInventoryItem);
                        return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_REGION_NOT_ENOUGH.getCode(),
                                OrderInventoryEnum.ORDER_INVENTORY_REGION_NOT_ENOUGH.getDesc(), orderInventory);
                    }

                    //如果实发仓库ID为空,说明总库存充足但是每个分仓仓库不足,则提示库存不足
                    if (Objects.nonNull(shouldWarehouseId) && Objects.isNull(actualWarehouseId)) {
                        orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventories.add(orderInventoryItem);
                        return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                                OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
                    } else if (Objects.nonNull(shouldWarehouseId) && Objects.nonNull(actualWarehouseId)) {
                        orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                        orderInventories.add(orderInventoryItem);
                    }

                    goodsStockLogDto.setWarehouseId(actualWarehouseId);
                    //封装分仓表的的实体信息
                    GoodsWarehouseStockDTO goodsWarehouseStockDto = new GoodsWarehouseStockDTO();
                    goodsWarehouseStockDto.setGoodsSkuId(goodsStock.getGoodsSkuId());
                    goodsWarehouseStockDto.setWarehouseId(actualWarehouseId);
                    goodsWarehouseStockDto.setSaleStockQuantity(goodsWarehouseStock.getSaleStockQuantity() - purchaseQuantity);
                    goodsWarehouseStockDto.setLockedStockQuantity(goodsWarehouseStock.getLockedStockQuantity() + purchaseQuantity);
                    goodsWarehouseStockDto.setPurchaseQuantity(purchaseQuantity);
                    goodsWarehouseStockDto.setVersion(goodsWarehouseStock.getVersion());
                    goodsStockDto.setGoodsWarehouseStock(goodsWarehouseStockDto);
                } else {//第三方商品
                    orderInventoryItem.setIsPre(StatusEnum.STATUS_NO.getCode());
                    goodsStockDto.setIsShelf(StatusEnum.STATUS_NO.getCode());

                    orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventories.add(orderInventoryItem);
                }
                goodsStockDto.setGoodsStockLog(goodsStockLogDto);
            } else {//总库存不足,则查看预售库存是否充足
                if (purchaseQuantity <= goodsStock.getPreStorage()) {
                    goodsStockDto.setPreStorage(goodsStock.getPreStorage() - purchaseQuantity);
                    goodsStockDto.setLockedStorage(goodsStock.getLockedStorage() + purchaseQuantity);
                    goodsStockDto.setPurchaseQuantity(purchaseQuantity);
                    goodsStockDto.setIsPre(StatusEnum.STATUS_YES.getCode());

                    GoodsStockLogDTO goodsStockLogDto = new GoodsStockLogDTO();
                    goodsStockLogDto.setGoodsId(goodsStock.getGoodsId());
                    goodsStockLogDto.setGoodsSkuId(goodsStock.getGoodsSkuId());
                    goodsStockLogDto.setLockedStorage(purchaseQuantity);
                    goodsStockLogDto.setInventoryType(InventoryType.VIRTUAL_INVENTORY.getCode());
                    goodsStockLogDto.setUserAccountId(orderItem.getUserAccountId());
                    goodsStockLogDto.setRequestTime(orderItem.getRequestTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                    goodsStockDto.setGoodsStockLog(goodsStockLogDto);

                    orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventoryItem.setShouldWarehouseId(shouldWarehouseId);
                    orderInventoryItem.setActualWarehouseId(actualWarehouseId);
                    orderInventoryItem.setIsPre(StatusEnum.STATUS_YES.getCode());
                    orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventories.add(orderInventoryItem);
                } else {
                    orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                    orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());

                    if (!skuDto.getGoodsType().equals(GoodsTypeEnum.GOODS_TYPE_THREE.getCode())) {
                        orderInventoryItem.setIsPre(StatusEnum.STATUS_YES.getCode());
                    } else {
                        orderInventoryItem.setIsPre(StatusEnum.STATUS_NO.getCode());
                    }
                    orderInventories.add(orderInventoryItem);

                    return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                            OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
                }
            }
            goodsStocks.add(goodsStockDto);
        }

        for (GoodsStockDTO goodsStockDto : goodsStocks) {
            int update = checkAndUpdateInventory(goodsStockDto, 0);

            if (!goodsStockDto.getIsPre().equals(StatusEnum.STATUS_YES.getCode())) {
                if (goodsStockDto.getIsShelf().equals(StatusEnum.STATUS_YES.getCode())) {
                    if (update > 0) {
                        GoodsWarehouseStockDTO goodsWarehouseStockDto = goodsStockDto.getGoodsWarehouseStock();
                        checkAndUpdateWarehouseInventory(goodsWarehouseStockDto, 0);
                    }
                }
            }

            GoodsStockLogDO goodsStockLog = new GoodsStockLogDO();
            goodsStockLog.setGoodsId(goodsStockDto.getGoodsId());
            goodsStockLog.setGoodsSkuId(goodsStockDto.getGoodsSkuId());
            goodsStockLog.setInventoryType(goodsStockDto.getGoodsStockLog().getInventoryType());
            goodsStockLog.setLockedStorage(goodsStockDto.getGoodsStockLog().getLockedStorage());
            goodsStockLog.setUserAccountId(goodsStockDto.getGoodsStockLog().getUserAccountId());
            goodsStockLog.setRequestTime(goodsStockDto.getGoodsStockLog().getRequestTime());
            goodsStockLog.setWarehouseId(goodsStockDto.getGoodsStockLog().getWarehouseId());
            goodsStockLogMapper.insert(goodsStockLog);

            for (OrderInventoryItemDTO orderInventoryItemDTO : orderInventory.getOrderInventoryItems()) {
                if (orderInventoryItemDTO.getSkuId().equals(goodsStockLog.getGoodsSkuId())) {
                    orderInventoryItemDTO.setLockedStockId(goodsStockLog.getId());
                }
            }
        }
        return ResponseResult.success(orderInventory);
    }

    /**
     * 通知库存中心，“提交订单”事件(Redis)
     *
     * @param orderItems 订单DTO集合
     * @return OrderInventoryDTO 处理结果
     * @Exception Exception 异常信息
     */
    @Override
    public ResponseResult<OrderInventoryDTO> informSubmitOrderEventV2(List<OrderItemDTO> orderItems) throws Exception {
        //封装给订单返回的数据实体
        OrderInventoryDTO orderInventory = new OrderInventoryDTO();
        List<OrderInventoryItemDTO> orderInventories = new ArrayList<>();
        orderInventory.setOrderInventoryItems(orderInventories);

        //获取订单中的商城skuid集合,查询商城服务获取商城sku的基本信息
        Map<Long, SkuDTO> skuMap = getLongSkuDTOMap(orderItems);
        logger.info("提交订单获取到的商品的信息:{}", skuMap);

        //lua脚本实现库存判断以及扣库存操作
        List<String> keyList = new ArrayList<>();
        for (OrderItemDTO orderItem : orderItems) {
            keyList.add(JSON.toJSONString(orderItem));
        }

        Map<String, Object> argvMap = new HashMap<>();
        argvMap.put("areaId", orderItems.get(0).getAreaId().intValue());

        List<JSONObject> skuStockList = (List<JSONObject>) redisTemplate.execute(redisScript, keyList, argvMap);

        //如果skuStockList返回null说明存在sku ID 不在redis上的情况,直接返回库存不足
        if (Objects.isNull(skuStockList)) {
            orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
            return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                    OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
        }

        Map<Long, JSONObject> skuStockMap = new HashMap<>(skuStockList.size());
        skuStockList.forEach(sku -> skuStockMap.put(sku.getLong("skuId"), sku));

        //遍历订单明细信息
        for (OrderItemDTO orderItem : orderItems) {
            SkuDTO skuDto = skuMap.get(orderItem.getGoodsSkuId());
            OrderInventoryItemDTO orderInventoryItem = buildOrderInventoryItem(skuDto);

            JSONObject jsonObject = skuStockMap.get(orderItem.getGoodsSkuId());
            Integer isEnough = jsonObject.getInteger("isEnough");
            Integer isPre = jsonObject.getInteger("isPre");
            Integer goodsType = jsonObject.getInteger("goodsType");
            Long shouldWarehouseId = jsonObject.getLong("shouldWarehouseId");
            Long actualWarehouseId = jsonObject.getLong("actualWarehouseId");
            Long lockedStockId = jsonObject.getLong("lockedStockId");

            orderInventoryItem.setLockedStockId(lockedStockId);

            if (isPre.equals(NumberConstant.NUMBER_ZERO.intValue())) {
                //非预售
                orderInventoryItem.setIsPre(isPre);
                if (!goodsType.equals(GoodsTypeEnum.GOODS_TYPE_THREE.getCode())) {
                    //自营
                    Integer allEnough = jsonObject.getInteger("allEnough");
                    if (allEnough.equals(NumberConstant.NUMBER_ONE.intValue())) {
                        if (isEnough.equals(NumberConstant.NUMBER_ONE.intValue())) {
                            orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                            orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                            orderInventoryItem.setShouldWarehouseId(shouldWarehouseId);
                            orderInventoryItem.setActualWarehouseId(actualWarehouseId);
                            orderInventories.add(orderInventoryItem);
                        } else {
                            orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                            orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                            orderInventories.add(orderInventoryItem);
                            return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_REGION_NOT_ENOUGH.getCode(),
                                    OrderInventoryEnum.ORDER_INVENTORY_REGION_NOT_ENOUGH.getDesc(), orderInventory);
                        }
                    } else {
                        orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventories.add(orderInventoryItem);
                        return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                                OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
                    }
                } else {
                    if (isEnough.equals(NumberConstant.NUMBER_ONE.intValue())) {
                        orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                        orderInventories.add(orderInventoryItem);
                    } else {
                        orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                        orderInventories.add(orderInventoryItem);
                        return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                                OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
                    }
                }
            } else {
                //预售
                if (isEnough.equals(NumberConstant.NUMBER_ONE.intValue())) {
                    orderInventory.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventoryItem.setIsPre(StatusEnum.STATUS_YES.getCode());
                    orderInventoryItem.setIsEnough(StatusEnum.STATUS_YES.getCode());
                    orderInventories.add(orderInventoryItem);
                } else {
                    orderInventory.setIsEnough(StatusEnum.STATUS_NO.getCode());
                    orderInventoryItem.setIsEnough(StatusEnum.STATUS_NO.getCode());
                    if (!skuDto.getGoodsType().equals(GoodsTypeEnum.GOODS_TYPE_THREE.getCode())) {
                        orderInventoryItem.setIsPre(StatusEnum.STATUS_YES.getCode());
                    } else {
                        orderInventoryItem.setIsPre(StatusEnum.STATUS_NO.getCode());
                    }
                    orderInventories.add(orderInventoryItem);

                    return ResponseResult.error(OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getCode(),
                            OrderInventoryEnum.ORDER_INVENTORY_NOT_ENOUGH.getDesc(), orderInventory);
                }
            }
        }
        return ResponseResult.success(orderInventory);
    }

    @NotNull
    private Map<Long, SkuDTO> getLongSkuDTOMap(List<OrderItemDTO> orderItems) {
        List<Long> goodsSkuIds = getGoodsSkuIds(orderItems);
        List<String> skuIdsKey = goodsSkuIds.stream().map(skuId -> "JUMI:SKU:".concat(skuId.toString())).collect(Collectors.toList());
        List<Object> skuList = redisTemplate.opsForValue().multiGet(skuIdsKey);

        Map<Long, SkuDTO> skuMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(skuList)) {
            skuList.stream().filter(Objects::nonNull).forEach(sku -> {
                SkuDTO skuDTO = (SkuDTO) sku;
                if (!skuMap.containsKey(skuDTO.getId())) {
                    skuMap.put(skuDTO.getId(), skuDTO);
                }
            });
        }

        List<Long> subSkuIds = new ArrayList<>(skuMap.keySet());
        if (goodsSkuIds.size() > subSkuIds.size()) {
            // 将redis没有的sku通过服务调用获取
            if (subSkuIds.size() > NumberConstant.NUMBER_ZERO.intValue()) {
                goodsSkuIds.removeAll(subSkuIds);
            }
            Map<Long, SkuDTO> skuMapDb = basicDataService.getSkuInfoByIds(goodsSkuIds);
            skuMap.putAll(skuMapDb);
        }
        return skuMap;
    }

    /**
     * 乐观锁方案扣减库存
     *
     * @param goodsStockDto 商品sku 库存
     * @return 更新的记录数
     */
    int decreaseStockForVersion(GoodsStockDTO goodsStockDto) {
        logger.info("乐观锁方案扣减库存goodsStockDto:" + goodsStockDto);

        LambdaQueryWrapper<GoodsStockDO> goodsStockWrapper = Wrappers.lambdaQuery();
        goodsStockWrapper.eq(goodsStockDto.getGoodsSkuId() != null, GoodsStockDO::getGoodsSkuId, goodsStockDto.getGoodsSkuId());
        GoodsStockDO goodsStock = goodsStockMapper.selectOne(goodsStockWrapper);

        if (goodsStockDto.getIsPre().equals(StatusEnum.STATUS_YES.getCode())) {
            goodsStock.setPreStorage(goodsStockDto.getPreStorage());
        } else {
            goodsStock.setGoodsSkuStorage(goodsStockDto.getGoodsSkuStorage());
        }
        goodsStock.setLockedStorage(goodsStockDto.getLockedStorage());
        //goodsStock.setVersion(goodsStockDto.getVersion());

        logger.info("乐观锁方案扣减库存goodsStock:" + goodsStock);
        return goodsStockMapper.updateById(goodsStock);
    }

    private int checkAndUpdateInventory(GoodsStockDTO goodsStockDto, int index) throws Exception {
        if (index >= 3) {
            //如果三次以上都没有更新成功则睡眠2毫秒
            Thread.sleep(3);
            logger.info("乐观锁方案扣减库存重试次数:" + index);
        }

        int update = decreaseStockForVersion(goodsStockDto);


        logger.info("乐观锁方案扣减库存更新标识:" + update);
        if (update < 1) {
            LambdaQueryWrapper<GoodsStockDO> goodsStockWrapper = Wrappers.lambdaQuery();
            goodsStockWrapper.eq(goodsStockDto.getGoodsSkuId() != null, GoodsStockDO::getGoodsSkuId, goodsStockDto.getGoodsSkuId());
            GoodsStockDO goodsStock = goodsStockMapper.selectOne(goodsStockWrapper);

            if (goodsStockDto.getIsPre().equals(StatusEnum.STATUS_YES.getCode())) {
                goodsStockDto.setPreStorage(goodsStock.getPreStorage() - goodsStockDto.getPurchaseQuantity());
            } else {
                goodsStockDto.setGoodsSkuStorage(goodsStock.getGoodsSkuStorage() - goodsStockDto.getPurchaseQuantity());
            }
            goodsStockDto.setLockedStorage(goodsStock.getLockedStorage() + goodsStockDto.getPurchaseQuantity());
            //goodsStockDto.setVersion(goodsStock.getVersion());
            return checkAndUpdateInventory(goodsStockDto, ++index);
        } else {
            return update;
        }
    }

    private int checkAndUpdateWarehouseInventory(GoodsWarehouseStockDTO goodsWarehouseStockDto,
                                                 int index) throws Exception {
        if (index >= 3) {
            //如果三次以上都没有更新成功则睡眠10毫秒
            Thread.sleep(10);
        }

        int update = decreaseWarehouseStockForVersion(goodsWarehouseStockDto);
        if (update < 1) {
            LambdaQueryWrapper<GoodsWarehouseStockDO> goodsStockWrapper = Wrappers.lambdaQuery();
            goodsStockWrapper.eq(GoodsWarehouseStockDO::getGoodsSkuId, goodsWarehouseStockDto.getGoodsSkuId());
            goodsStockWrapper.eq(GoodsWarehouseStockDO::getWarehouseId, goodsWarehouseStockDto.getWarehouseId());
            GoodsWarehouseStockDO goodsWarehouseStock = goodsWarehouseStockMapper.selectOne(goodsStockWrapper);

            goodsWarehouseStock.setSaleStockQuantity(goodsWarehouseStock.getSaleStockQuantity() - goodsWarehouseStockDto.getPurchaseQuantity());
            goodsWarehouseStock.setLockedStockQuantity(goodsWarehouseStock.getSaleStockQuantity() + goodsWarehouseStockDto.getPurchaseQuantity());
            return checkAndUpdateWarehouseInventory(goodsWarehouseStockDto, ++index);
        } else {
            return update;
        }
    }

    int decreaseWarehouseStockForVersion(GoodsWarehouseStockDTO goodsWarehouseStockDto) {
        LambdaQueryWrapper<GoodsWarehouseStockDO> goodsStockWrapper = Wrappers.lambdaQuery();
        goodsStockWrapper.eq(GoodsWarehouseStockDO::getGoodsSkuId, goodsWarehouseStockDto.getGoodsSkuId());
        goodsStockWrapper.eq(GoodsWarehouseStockDO::getWarehouseId, goodsWarehouseStockDto.getWarehouseId());
        GoodsWarehouseStockDO goodsWarehouseStock = goodsWarehouseStockMapper.selectOne(goodsStockWrapper);

        goodsWarehouseStock.setSaleStockQuantity(goodsWarehouseStockDto.getSaleStockQuantity());
        goodsWarehouseStock.setLockedStockQuantity(goodsWarehouseStockDto.getLockedStockQuantity());
        //goodsWarehouseStock.setVersion(goodsWarehouseStockDto.getVersion());
        return goodsWarehouseStockMapper.updateById(goodsWarehouseStock);
    }

    protected List<Long> getGoodsSkuIds(List<OrderItemDTO> orderItems) {
        List<Long> goodsSkuIds = new ArrayList<Long>();
        for (OrderItemDTO orderItemDTO : orderItems) {
            goodsSkuIds.add(orderItemDTO.getGoodsSkuId());
        }
        return goodsSkuIds;
    }

    protected OrderInventoryItemDTO buildOrderInventoryItem(SkuDTO skuDto) {
        OrderInventoryItemDTO orderInventoryItem = new OrderInventoryItemDTO();
        orderInventoryItem.setGoodsId(skuDto.getGoodsId());
        orderInventoryItem.setSkuId(skuDto.getId());
        orderInventoryItem.setGoodsSkuName(skuDto.getGoodsName());
        orderInventoryItem.setSpecName(skuDto.getSpecName());
        orderInventoryItem.setGoodsPropertyImage(skuDto.getGoodsPropertyImage());
        orderInventoryItem.setSupplyId(skuDto.getSupplyId());
        orderInventoryItem.setCateId(skuDto.getCateId());
        orderInventoryItem.setGoodsPrice(skuDto.getGoodsPrice());
        orderInventoryItem.setPlatinumPrice(skuDto.getPlatinumPrice());
        orderInventoryItem.setDiamondsPrice(skuDto.getDiamondsPrice());
        orderInventoryItem.setKingPrice(skuDto.getKingPrice());
        orderInventoryItem.setGetOrderPrice(skuDto.getGetOrderPrice());
        orderInventoryItem.setGoodsCostprice(skuDto.getGoodsCostprice());
        orderInventoryItem.setGoodsGrossWeight(skuDto.getGoodsGrossWeight());
        orderInventoryItem.setIsCompose(skuDto.getGoodsType().equals(GoodsTypeEnum.GOODS_TYPE_GROUP.getCode())
                ? StatusEnum.STATUS_YES.getCode() : StatusEnum.STATUS_NO.getCode());
        orderInventoryItem.setIsShelf(skuDto.getGoodsType().equals(GoodsTypeEnum.GOODS_TYPE_THREE.getCode())
                ? StatusEnum.STATUS_NO.getCode() : StatusEnum.STATUS_YES.getCode());
        orderInventoryItem.setFreightTempDTO(skuDto.getFreightTempDTO());
        orderInventoryItem.setDrainage(skuDto.getDrainage());
        orderInventoryItem.setIsImport(skuDto.getIsImport());
        orderInventoryItem.setGoodCountry(skuDto.getGoodCountry());
        orderInventoryItem.setFixRebate(skuDto.getFixRebate());
        return orderInventoryItem;
    }

    /**
     * 通知库存中心，“取消订单”事件
     *
     * @param orderDTO 订单DTO
     * @return 处理结果
     */
    @Override
    public Boolean informCancelOrderEvent(OrderItemDTO orderDTO) {
        logger.info("取消订单传的订单信息:{}", JSONObject.toJSONString(orderDTO));
        //判断订单是否存在redis中 存在即已经操作过取消
        if (redisTemplate.hasKey(OrderOperationConstant.IF_ORDER_SUPPLY_KEY + orderDTO.getOrderNo() + "-" + orderDTO.getGoodsSkuId())) {
            return true;
        }
        StockUpdater stockUpdater = cancelOrderUpdaterFactory.create(orderDTO);
        stockUpdater.updateGoodsStock();
        //取消操作成功后 订单存入redis
        redisTemplate.opsForValue().set(OrderOperationConstant.IF_ORDER_SUPPLY_KEY + orderDTO.getOrderNo() + "-" + orderDTO.getGoodsSkuId(), "1");
        return true;
    }

    /**
     * 订单-预售转真实 返回转真实成功的dto
     * 补货库存-数量
     *
     * @param orderDTO 订单DTO
     * @return
     */
    @Override
    public ResponseResult preSaleOrderEvent(OrderWareStockDTO orderDTO) {
        logger.info("预售转实际库存参数:{}", JSONObject.toJSONString(orderDTO));
        //返回成功操作集合
        List<OrderReplenishDTO> orders = new ArrayList<>();
        //订单集合信息
        List<OrderReplenishDTO> orderReplenishes = orderDTO.getOrderReplenishes();
        //sku分仓库存信息
        List<OrderGoodsSkuWareDTO> orderGoodsSkuWares = orderDTO.getOrderGoodsSkuWares();
        ExceptionEnum.FAIL_ORDER_SUPPLY_SKU_NULL.doThrowIf(CollectionUtil.isEmpty(orderReplenishes) || CollectionUtil.isEmpty(orderGoodsSkuWares));
        //先获取sku所有的分仓信息
        List<GoodsWarehouseStockDO> warehouseStockes = goodsWarehouseStockMapper.selectList(
                Wrappers.<GoodsWarehouseStockDO>lambdaQuery()
                        .in(GoodsWarehouseStockDO::getGoodsSkuId, orderGoodsSkuWares.stream().map(osku -> {
                            return osku.getSkuId();
                        }).collect(Collectors.toList())));
        //20201205需求梳理: stockmap来接收所有sku转的数量
        Map<Long, Integer> stockMap = new HashMap<>();
        //20210108 添加map来存储redis的库存操作
        Map<String, Integer> redisMap = new HashMap<>();
        Map<Long, OrderWareReplishStockDTO> wareMap = packOrderWareStockUpdateMap(orderReplenishes, orderGoodsSkuWares, warehouseStockes, orders, stockMap, redisMap);
        logger.info("预售转实际计算仓库后参数:{}", JSONObject.toJSONString(wareMap));
        if (CollectionUtil.isNotEmpty(wareMap)) {
            wareMap.forEach((k, v) -> {
                GoodsWarehouseStockDO goodsWarehouseStockDO = new GoodsWarehouseStockDO();
                goodsWarehouseStockDO.setId(k);
                goodsWarehouseStockDO.setReplenishStorage(v.getReplishNum());
                //20201205 预售+ 实际-
                goodsWarehouseStockDO.setSaleStockQuantity(v.getStockNum());
                goodsWarehouseStockMapper.updateById(goodsWarehouseStockDO);
            });
            //20210108 同步redis分仓  线上暂时注释
//            if(CollectionUtil.isNotEmpty(redisMap)){
//                redisMap.forEach((k, v)->{
//                    //预售订单中 分仓只需要把分仓中的实际库存减去
//                    if(redisTemplate.hasKey(k)) {
//                        redisSkuStockInc(k, InventoryRedisKey.SALESTOCKQUANTITY, -v);
//                    }
//                });
//            }
        }
        //20201205需求梳理: 需要添加sku总库存的添加 -
        if (CollectionUtil.isNotEmpty(stockMap)) {
            stockMap.forEach((k, v) -> {
                GoodsStockDO goodsStockDO = goodsStockMapper.selectOne(
                        Wrappers.<GoodsStockDO>lambdaQuery()
                                .eq(GoodsStockDO::getGoodsSkuId, k));
                if (goodsStockDO != null) {
                    //20201205 sku的总库存 预售+ 实际-
                    goodsStockDO.setPreStorage(goodsStockDO.getPreStorage() + v);
                    goodsStockDO.setGoodsSkuStorage(goodsStockDO.getGoodsSkuStorage() - v);
                    goodsStockMapper.updateById(goodsStockDO);
                    //20210108 同步redis的库存 线上暂时注释
//                    if(redisTemplate.hasKey(InventoryRedisKey.GOODS_SKU_STOCK + goodsStockDO.getGoodsSkuId())) {
//                        redisSkuStockInc(InventoryRedisKey.GOODS_SKU_STOCK + goodsStockDO.getGoodsSkuId(),
//                                InventoryRedisKey.PRESTORAGE, v);
//                        redisSkuStockInc(InventoryRedisKey.GOODS_SKU_STOCK + goodsStockDO.getGoodsSkuId(),
//                                InventoryRedisKey.GOODSSKUSTORAGE, -v);
//                    }
                }
            });
        }
        return ResponseResult.success(orders);
    }

    /**
     * 通知库存中心，“订单调仓”事件
     *
     * @param transferWarehouses 订单调仓信息DTO集合
     * @return Boolean 处理结果
     */
    @Override
    public Boolean transferWarehouseEvent(List<TransferWarehouseDTO> transferWarehouses) {
        LocalDateTime localDateTime = LocalDateTime.now();
        boolean transfer = false;
        for (TransferWarehouseDTO transferWarehouse : transferWarehouses) {
            LambdaQueryWrapper<GoodsWarehouseStockDO> newWareHouseWrapper = Wrappers.lambdaQuery();
            newWareHouseWrapper.eq(GoodsWarehouseStockDO::getGoodsSkuId, transferWarehouse.getGoodsSkuId());
            newWareHouseWrapper.eq(GoodsWarehouseStockDO::getWarehouseId, transferWarehouse.getNewWarehouseId());
            GoodsWarehouseStockDO newWareHouseStock = goodsWarehouseStockMapper.selectOne(newWareHouseWrapper);

            if (newWareHouseStock == null) {
                break;
            }

            if (transferWarehouse.getSaleStockQuantity() <= newWareHouseStock.getSaleStockQuantity()) {
                LambdaQueryWrapper<GoodsWarehouseStockDO> oldWareHouseWrapper = Wrappers.lambdaQuery();
                oldWareHouseWrapper.eq(GoodsWarehouseStockDO::getGoodsSkuId, transferWarehouse.getGoodsSkuId());
                oldWareHouseWrapper.eq(GoodsWarehouseStockDO::getWarehouseId, transferWarehouse.getOldWarehouseId());
                GoodsWarehouseStockDO oldWareHouseStock = goodsWarehouseStockMapper.selectOne(oldWareHouseWrapper);

                if (oldWareHouseStock == null) {
                    break;
                }

                oldWareHouseStock.setSaleStockQuantity(oldWareHouseStock.getSaleStockQuantity() + transferWarehouse.getSaleStockQuantity());
                oldWareHouseStock.setSaledStockQuantity(oldWareHouseStock.getSaledStockQuantity() - transferWarehouse.getSaleStockQuantity());
                oldWareHouseStock.setUpdateTime(localDateTime);
                oldWareHouseStock.setVersion(oldWareHouseStock.getVersion());
                int updateOldWarehouse = goodsWarehouseStockMapper.updateById(oldWareHouseStock);

                newWareHouseStock.setSaleStockQuantity(newWareHouseStock.getSaleStockQuantity() - transferWarehouse.getSaleStockQuantity());
                newWareHouseStock.setSaledStockQuantity(newWareHouseStock.getSaledStockQuantity() + transferWarehouse.getSaleStockQuantity());
                newWareHouseStock.setUpdateTime(localDateTime);
                newWareHouseStock.setVersion(newWareHouseStock.getVersion());
                int updateNewWarehouse = goodsWarehouseStockMapper.updateById(newWareHouseStock);

                if ((updateOldWarehouse + updateNewWarehouse) == NumberConstant.NUMBER_TWO.intValue()) {
                    transfer = true;
                } else {
                    transfer = false;
                    break;
                }
            } else {
                transfer = false;
                break;
            }
        }
        return transfer;
    }

    /**
     * 订单管理-审核订单校验库存量
     *
     * @param supplyStockDeduct
     * @return true: 库存充足, false: 库存不足
     */
    @Override
    public Boolean deductStockBySkuId(SupplyStockDeductDTO supplyStockDeduct) {
        boolean b = true;
        GoodsWarehouseStockDO goodsWarehouseStockDO = goodsWarehouseStockMapper.selectOne(
                Wrappers.<GoodsWarehouseStockDO>lambdaQuery()
                        .eq(GoodsWarehouseStockDO::getGoodsSkuId, supplyStockDeduct.getSkuId())
                        .eq(GoodsWarehouseStockDO::getWarehouseId, supplyStockDeduct.getWareId()));
        if (goodsWarehouseStockDO != null) {
            if (supplyStockDeduct.getStockNum() > goodsWarehouseStockDO.getSaleStockQuantity()) {
                b = false;
            }
        }
        return b;
    }

    /**
     * 封装更新分仓sku的补货(预售转实际库存)
     *
     * @param orderReplenishes   订单sku库存信息
     * @param orderGoodsSkuWares 分仓sku库存信息
     * @param warehouseStockes   实际分仓库存信息
     * @param orders             返回预售转成功
     * @return
     */
    private Map<Long, OrderWareReplishStockDTO> packOrderWareStockUpdateMap(List<OrderReplenishDTO> orderReplenishes, List<OrderGoodsSkuWareDTO> orderGoodsSkuWares,
                                                                            List<GoodsWarehouseStockDO> warehouseStockes, List<OrderReplenishDTO> orders,
                                                                            Map<Long, Integer> stockMap, Map<String, Integer> redisMap) {
        Map<Long, OrderWareReplishStockDTO> map = new HashMap<>();
        orderReplenishes.stream().forEach(oSku -> {
            //取预售转实际添加的库存
            List<OrderGoodsSkuWareDTO> orderGoodsSkuWareDTO = orderGoodsSkuWares.stream().filter(or -> oSku.getSkuId().equals(or.getSkuId())).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(orderGoodsSkuWareDTO)) {
                //判断订单中的sku库存是否小于添加的sku
                orderGoodsSkuWareDTO.stream().anyMatch(sku -> {
                    if (oSku.getStorage() <= sku.getAddStock()) {
                        //预售转真实库存计算
                        //1. 取订单dto中的sku及库存量获取出订单传输的sku分仓的添加库存
                        //2. 订单dto中的库存量比对sku分仓的添加库存 如果订单的<sku分仓的,sku分仓-订单的 否则不做操作
                        //3. 2校验通过后 获取出sku分仓在商城数据库中的实际分仓 然后存入map中 后续根据map来更新库存服务分仓的补货量(转实际量)
                        //备注:  返回的map中 key为库存服务中sku分仓记录的主键id  value为更新的补货量 补货量会根据上面的校验来累加订单中sku的库存
                        sku.setAddStock(sku.getAddStock() - oSku.getStorage());
                        GoodsWarehouseStockDO goodsWarehouseStockDO =
                                warehouseStockes.stream().filter(ware -> sku.getSkuId()
                                        .equals(ware.getGoodsSkuId()) && sku.getWareId().equals(ware.getWarehouseId()))
                                        .findAny().orElse(null);
                        if (goodsWarehouseStockDO != null) {
                            //用于返回sku分仓的补货量和实际库存量(计算后的值)
                            OrderWareReplishStockDTO orderWareReplishStock = new OrderWareReplishStockDTO();
                            orderWareReplishStock.setReplishNum(goodsWarehouseStockDO.getReplenishStorage() + oSku.getStorage());
                            orderWareReplishStock.setStockNum(goodsWarehouseStockDO.getSaleStockQuantity() - oSku.getStorage());
                            map.put(goodsWarehouseStockDO.getId(), orderWareReplishStock);
                            oSku.setWarehouseId(goodsWarehouseStockDO.getWarehouseId());
                            orders.add(oSku);
                            //20210108 处理redis的分仓库存
//                            redisMap.put(InventoryRedisKey.GOODS_SKU_WARE_STOCK+goodsWarehouseStockDO.getGoodsSkuId()+":"+goodsWarehouseStockDO.getWarehouseId(),oSku.getStorage());
                        }
                        if (stockMap.get(sku.getSkuId()) != null) {
                            stockMap.put(sku.getSkuId(), stockMap.get(sku.getSkuId()) + oSku.getStorage());
                        } else {
                            stockMap.put(sku.getSkuId(), oSku.getStorage());
                        }
                        return true;
                    }
                    return false;
                });
            }
        });
        return map;
    }

    /**
     * 更新库存的库存值 自增
     * @param key redis的库存key
     * @param mapKey 商品库存的map中key
     * @param stockNum 库存量
     */
    private void redisSkuStockInc(String key, String mapKey ,Integer stockNum){
        redisTemplate.opsForHash().increment(key, mapKey, stockNum);
    }
}
